1 Load libraries and settings

suppressPackageStartupMessages(library(scMET))
suppressPackageStartupMessages(library(BASiCS))
suppressPackageStartupMessages(library(SingleCellExperiment))
suppressPackageStartupMessages(library(ggpubr))
suppressPackageStartupMessages(library(data.table))
suppressPackageStartupMessages(library(purrr))
suppressPackageStartupMessages(library(viridis))
suppressPackageStartupMessages(library(ggplot2))

#####################
## Define settings ##
#####################
source("../load_settings.R")
data_dir <- "/Users/ckapoura/datasets/scMET_ms/gastrulation/data/"
hits_dir <- "/Users/ckapoura/datasets/scMET_ms/gastrulation/metrna/hits/"
pdf_dir <- "/Users/ckapoura/datasets/scMET_ms/gastrulation/metrna/analysis/"
if (!dir.exists(hits_dir)) {dir.create(hits_dir, recursive = TRUE)}
if (!dir.exists(pdf_dir)) {dir.create(pdf_dir, recursive = TRUE)}

# Define genomic context
opts$anno <- c("prom_2000_2000")
#opts$anno <- c("first_intron")

2 RNA variability

2.1 Load BASiCS object and get posterior summary stats

# Load BASiCS object
basics_obj <- readRDS(file = paste0(data_dir, "/rna_basics.rds"))
# SingleCellExperiment object
sce <- readRDS(io$rna)[, sample_metadata$id_rna]
# Perform HVG analysis
HVG <- BASiCS_DetectVG(basics_obj, PercentileThreshold = 0.9,
                       EFDR = 0.01, Plot = FALSE)
Posterior probability threshold has been supplied 
EFDR will not be calibrated. 
hvg_results <- HVG@Table[, c("GeneName", "Prob", "HVG")]
colnames(hvg_results) <- c("ens_id", "rna_tail_prob", "rna_is_variable")

# Obtain posterior draws summary
chain_summary <- Summary(basics_obj)

# Merge Ensemb IDs with gene symbol
subset_meta <- rowData(sce[rownames(chain_summary@parameters$mu)]) %>%
  as.data.table(keep.rownames = "ens_id") %>%
  merge(., hvg_results, by = "ens_id")
Arguments in '...' ignored
# Get posterior medians summary
post_summary <- data.table(ens_id = rownames(chain_summary@parameters$mu),
                           rna_mu = log(chain_summary@parameters$mu[, 1]),
                           rna_epsilon = chain_summary@parameters$epsilon[, 1],
                           rna_delta = log(chain_summary@parameters$delta[, 1]) )

2.2 Plot RNA variability

2.2.1 Mean - overdispersion with trend line

# Show mean-overdispersion relationship
plot(BASiCS_ShowFit(basics_obj))

2.2.2 Mean - overdispersion with HVG analysis

# Combine data 
joint_rna_dt <- merge(subset_meta, post_summary, by = "ens_id") %>%
  setorder(-rna_epsilon)

to.plot <- joint_rna_dt[rna_mu > 0.1]
tmp <- to.plot[to.plot$rna_is_variable == FALSE, ]
tmp <- tmp[sample(NROW(tmp), 2000), ]
to.plot <- rbind(to.plot[to.plot$rna_is_variable == TRUE, ], tmp)

mode <- c("rna_delta", "rna_epsilon") 
y_lab <- list(
  "rna_delta" = "RNA log overdispersion",
  "rna_epsilon" = "RNA residual overdispersion"
)
for (m in mode) {
  gg <- ggplot(to.plot, aes_string(x = "rna_mu", y = m)) +
    geom_point(aes(fill = rna_is_variable, alpha = rna_is_variable, size = rna_is_variable),
               shape = 21, stroke = 0.01) +
    scale_fill_manual(values = c("gray80","red")) +
    scale_alpha_manual(values = c(0.4, 0.6)) +
    scale_size_manual(values = c(0.65, 1.6)) +
    guides(fill = guide_legend(override.aes = list(size = 2.5))) +
    xlab("Log mean expression") + ylab(y_lab[[m]]) +
    ggrepel::geom_text_repel(data = head(to.plot[rna_is_variable == TRUE], n = 20),
                             aes_string(x = "rna_mu", y = m, label = "symbol"),
                             size = 3, color = "black") +
    theme_classic() +
    theme(
      legend.position = "none",
      strip.text = element_text(color = "black", size = rel(1.1)),
      legend.text = element_text(color = "black", size = rel(1.15)),
      axis.text = element_text(color = "black", size = rel(1)),
      axis.title = element_text(color = "black", size = rel(1.2))
    )
  print(gg)
  pdf(file = paste0(pdf_dir, "basics_meanvar_", m, ".pdf"), 
      width = 7, height = 5, useDingbats = FALSE)
  print(gg)
  dev.off()
}

2.2.3 Store HVG results

rna_hvg <- joint_rna_dt[, c("ens_id", "symbol", "rna_tail_prob", "rna_is_variable",
                  "rna_mu", "rna_epsilon", "rna_delta")] %>%
  setnames(c("symbol"), c("rna_symbol"))
fwrite(rna_hvg, paste0(hits_dir,"/rna_hvg.txt.gz"), sep = "\t", quote = FALSE)

3 DNAm variability

# Perform HVF analysis
obj <- readRDS(sprintf("%s/%s_vb.rds", data_dir, opts$anno))
scmet_obj <- scmet_hvf(scmet_obj = obj, delta_e = 0.9, delta_g = NULL, efdr = 0.1)
For HVF detection task:
Evidence probability threshold chosen via EFDR valibration is too low. 
Probability threshold set automatically equal to 'evidence_thresh'.

641 Features classified as highly variable using: 
- The  90 percentile of variable genes 
- Evidence threshold = 0.8
- EFDR = 6.74% 
- EFNR = 9.78% 
# Obtain gene names
gene_metadata <- fread(io$gene.metadata) %>% .[,c("ens_id", "symbol")]
#Highlight top hits for promoters
met_joint <- scmet_obj$hvf$summary %>% as.data.table %>%
  setnames("feature_name", "ens_id") %>%
  merge(gene_metadata, by = "ens_id") %>%
  setorder(-epsilon)

3.1 Mean - overdispersion with HVF analysis

mode <- c("epsilon", "gamma")
for (m in mode) {
  
  gg <- scmet_plot_mean_var(obj = scmet_obj, y = m, task = "hvf",
                            nfeatures = 3000) +
    theme(legend.position = c(0.9, 0.1)) +
  ggrepel::geom_text_repel(data = head(met_joint, n = 35),
                           aes_string(x = "mu", y = m, label = "symbol"),
                           size = 2.3, color = "black")
  print(gg)
  pdf(paste0(pdf_dir, "/scmet_meanvar_", opts$anno, "_", m, ".pdf"),
      width = 7, height = 5, useDingbats = FALSE)
  print(gg)
  dev.off()
}

3.2 Store HVF analysis results

met_hvf <- scmet_obj$hvf$summary %>%
  as.data.table %>% .[, anno := opts$anno] %>%
  setnames("feature_name", "ens_id")
fwrite(met_hvf, paste0(hits_dir, "/met_hvf_", opts$anno, ".txt.gz"), sep = "\t", quote = FALSE)

4 Joint analysis

## Combine omics
metrna <- merge(met_hvf, rna_hvg, by = "ens_id") %>% 
  merge(gene_metadata, by = "ens_id")

# Process
metrna[, color := "black"]
metrna[epsilon > 0.5 & rna_epsilon > 1.5, color := "red"]
metrna[epsilon < 0.15 & rna_epsilon > 2, color := "blue"]
metrna <- metrna %>% setorder(-rna_epsilon)
fwrite(metrna, file = paste0(hits_dir, "metrna_variability_", opts$anno, ".csv"), 
       sep = "\t", quote = FALSE)

4.1 Plot RNA and DNA co-variability

set.seed(12)
# Marker genes
marker_genes <- c(
  "Cubn", "Cer1", "Mixl1", "Lefty2", "Amn", "Cldn6", "Mesp1",
  "Krt8", "Apob", "Foxi1", "Aplnr", "Id3", "Peg3",  "Dppa5a", "Dppa4",
  "Dppa2", "Spp1", "Morc1", "Trap1a", "Pcdh19", "Zfp42", "Fmr1nb"
)
if (opts$anno == "prom_2000_2000") {
  main_txt <- "Promoter region"
} else if (opts$anno == "first_exon") {
  main_txt <- "First exon" 
} else {
  main_txt <- "First intron"
}
#main_txt <- ifelse(opts$anno == "prom_2000_2000", "Promoter region", "First exon")
# Create copy
metrna_plot <- copy(metrna)
tmp <- metrna_plot[metrna_plot$color == "black", ]
tmp <- tmp[sample(NROW(tmp), ifelse(opts$anno == "prom_2000_2000", 4000, 1000)), ]
to.plot <- rbind(metrna_plot[metrna_plot$color != "black", ], tmp)
gg <- ggplot(to.plot, aes(x = epsilon, y = rna_epsilon, 
                        alpha = color, fill = color, size = color)) +
  labs(x = expression(paste("Residual overdispersion (DNAm)")),
       y = "Residual overdispersion (RNA)") +
  geom_point(shape = 21, stroke = 0.1, color = "black") +
  scale_fill_manual(values = c("black" = "gray80", "red" = "#4E934D", "blue" = "#C400AD")) +
  scale_size_manual(values = c("black" = 0.6, "red" = 2, "blue" = 2)) +
  scale_alpha_manual(values = c("black" = 0.5, "red" = 0.8, "blue" = 0.8)) +
  ggrepel::geom_text_repel(data = to.plot[color == "red" & symbol %in% marker_genes],
                           aes(x = epsilon, y = rna_epsilon, label = symbol),
                           size = 5, color = "#4E934D") +
  ggrepel::geom_text_repel(data = to.plot[color == "blue" & symbol %in% marker_genes],
                           aes(x = epsilon,  y = rna_epsilon, label = symbol),
                           size = 5, color = "#C400AD") +
  coord_cartesian(xlim = c(-0.9, 1.6)) +
  theme_classic() +
  ggtitle(main_txt) +
  theme(
    legend.position = "none",
    plot.title = element_text(hjust = 0.5, face = "bold", color = "black", size = rel(1.45)),
    strip.text = element_text(color = "black", size = rel(1.1)),
    legend.text = element_text(color = "black", size = rel(1.15)),
    axis.text = element_text(color = "black", size = rel(1.05)),
    axis.title = element_text(color = "black", size = rel(1.3))
  )
print(gg)

pdf(paste0(pdf_dir, "/metrna_variability_", opts$anno, ".pdf"), width = 10, height = 4, useDingbats = FALSE)
print(gg)
dev.off()
quartz_off_screen 
                2 

4.2 Plot RNA and DNA mean levels

set.seed(12)
# Marker genes
marker_genes <- c(
  "Cubn", "Cer1", "Mixl1", "Lefty2", "Amn", "Cldn6", "Mesp1",
  "Krt8", "Apob", "Foxi1", "Aplnr", "Id3", "Peg3",  "Dppa5a", "Dppa4",
  "Dppa2", "Spp1", "Morc1", "Trap1a", "Pcdh19", "Zfp42", "Fmr1nb"
)

#main_txt <- ifelse(opts$anno == "prom_2000_2000", "Promoter region", "First exon")
if (opts$anno == "prom_2000_2000") {
  main_txt <- "Promoter region"
} else if (opts$anno == "first_exon") {
  main_txt <- "First exon" 
} else {
  main_txt <- "First intron"
}
# Create copy
metrna_plot <- copy(metrna)
tmp <- metrna_plot[metrna_plot$color == "black", ]
tmp <- tmp[sample(NROW(tmp), ifelse(opts$anno == "prom_2000_2000", 4000, 1000)), ]
to.plot <- rbind(metrna_plot[metrna_plot$color != "black", ], tmp)
gg <- ggplot(to.plot, aes(x = mu, y = rna_mu, 
                        alpha = color, fill = color, size = color)) +
  labs(x = expression(paste("Mean DNA methylation")),
       y = "Mean RNA expression") +
  geom_point(shape = 21, stroke = 0.1, color = "black") +
  scale_fill_manual(values = c("black" = "gray80", "red" = "#4E934D", "blue" = "#C400AD")) +
  scale_size_manual(values = c("black" = 0.6, "red" = 2, "blue" = 2)) +
  scale_alpha_manual(values = c("black" = 0.5, "red" = 0.8, "blue" = 0.8)) +
  ggrepel::geom_text_repel(data = to.plot[color == "red" & symbol %in% marker_genes],
                           aes(x = mu, y = rna_mu, label = symbol),
                           size = 5, color = "#4E934D") +
  ggrepel::geom_text_repel(data = to.plot[color == "blue" & symbol %in% marker_genes],
                           aes(x = mu,  y = rna_mu, label = symbol),
                           size = 5, color = "#C400AD") +
  annotate("text", y = 9, x = 0.88,label="R = -0.4",hjust = 1) +
  #geom_smooth(method=lm, se=FALSE) +
  #coord_cartesian(xlim = c(, 1.6)) +
  theme_classic() +
  ggtitle(main_txt) +
  theme(
    legend.position = "none",
    plot.title = element_text(hjust = 0.5, face = "bold", color = "black", size = rel(1.45)),
    strip.text = element_text(color = "black", size = rel(1.1)),
    legend.text = element_text(color = "black", size = rel(1.15)),
    axis.text = element_text(color = "black", size = rel(1.05)),
    axis.title = element_text(color = "black", size = rel(1.3))
  )
print(gg)

pdf(paste0(pdf_dir, "/metrna_mean_", opts$anno, ".pdf"), width = 8, height = 5, useDingbats = FALSE)
print(gg)
dev.off()
quartz_off_screen 
                2 

4.3 Plot RNA and DNA mean levels

set.seed(12)
# Get density of points in 2 dimensions.
# @param x A numeric vector.
# @param y A numeric vector.
# @param n Create a square n by n grid to compute density.
# @return The density within each square.
.get_density <- function(x, y, ...) {
  dens <- MASS::kde2d(x, y, ...)
  ix <- findInterval(x, dens$x)
  iy <- findInterval(y, dens$y)
  ii <- cbind(ix, iy)
  return(dens$z[ii])
}

# ggplot(parameter_df[ind_not_na, ], aes(log10(cv2_scran), log10(delta))) + geom_pointdensity() + scale_colour_viridis(name = "Density")
# library("ggpointdensity")
# library("viridis")

# Marker genes
marker_genes <- c(
  "Cubn", "Cer1", "Mixl1", "Lefty2", "Amn", "Cldn6", "Mesp1",
  "Krt8", "Apob", "Foxi1", "Aplnr", "Id3", "Peg3",  "Dppa5a", "Dppa4",
  "Dppa2", "Spp1", "Morc1", "Trap1a", "Pcdh19", "Zfp42", "Fmr1nb"
)

main_txt <- ifelse(opts$anno == "prom_2000_2000", "Promoter region", "First exon")
# Create copy
metrna_plot <- copy(metrna)
tmp <- metrna_plot[metrna_plot$color == "black", ]
#tmp <- tmp[sample(NROW(tmp), ifelse(opts$anno == "prom_2000_2000", 4000, 1000)), ]
to.plot <- metrna_plot# rbind(metrna_plot[metrna_plot$color != "black", ], tmp)

to.plot <- to.plot %>% .[, density := .get_density(log(mu), rna_mu, n = 50)]
gg <- ggplot(to.plot, aes(x = log(mu), y = rna_mu, color = density)) +
  labs(x = expression(paste("Log mean DNA methylation")),
       y = "Log mean RNA expression") +
  geom_point(size = 1) +
  viridis::scale_fill_viridis() +
  viridis::scale_color_viridis() +
  # ggrepel::geom_text_repel(data = to.plot[color == "red" & symbol %in% marker_genes],
  #                          aes(x = log(mu), y = rna_mu, label = symbol),
  #                          size = 5, color = "#4E934D") +
  # ggrepel::geom_text_repel(data = to.plot[color == "blue" & symbol %in% marker_genes],
  #                          aes(x = log(mu),  y = rna_mu, label = symbol),
  #                          size = 5, color = "#C400AD") +
  #annotate("text", y = 9, x = 0.88,label="R = -0.4",hjust = 1) +
  annotate("text", y = 9, x = 0,label="R = -0.4",hjust = 1) +
  #geom_smooth(method=lm, se=FALSE) +
  #coord_cartesian(xlim = c(, 1.6)) +
  theme_classic() +
  ggtitle(main_txt) +
  theme(
    legend.position = "none",
    plot.title = element_text(hjust = 0.5, face = "bold", color = "black", size = rel(1.45)),
    strip.text = element_text(color = "black", size = rel(1.1)),
    legend.text = element_text(color = "black", size = rel(1.15)),
    axis.text = element_text(color = "black", size = rel(1.05)),
    axis.title = element_text(color = "black", size = rel(1.3))
  )
print(gg)

pdf(paste0(pdf_dir, "/metrna_mean_heatmap_", opts$anno, ".pdf"), width = 8, height = 5, useDingbats = FALSE)
print(gg)
dev.off()
quartz_off_screen 
                2 

LS0tCnRpdGxlOiAic2NOTVQtc2VxIGdhc3RydWxhdGlvbiBhbmFseXNpcyIKYXV0aG9yOiAiQy5BLkthcG91cmFuaSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogY2VydWxlYW4KICAgIHRvYzogeWVzCi0tLQoKYGBge3Igc2V0X2dsb2JhbF9vcHRpb25zLCBjYWNoZT1GQUxTRSwgcmVzdWx0cz0naGlkZScsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoJ2tuaXRyJykKa25pdHI6Om9wdHNfY2h1bmskc2V0KGRwaSA9IDc1LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKYGBgCgojIExvYWQgbGlicmFyaWVzIGFuZCBzZXR0aW5ncwpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoc2NNRVQpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShCQVNpQ1MpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KGdncHVicikpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KGRhdGEudGFibGUpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShwdXJycikpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHZpcmlkaXMpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyBEZWZpbmUgc2V0dGluZ3MgIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnNvdXJjZSgiLi4vbG9hZF9zZXR0aW5ncy5SIikKZGF0YV9kaXIgPC0gIi9Vc2Vycy9ja2Fwb3VyYS9kYXRhc2V0cy9zY01FVF9tcy9nYXN0cnVsYXRpb24vZGF0YS8iCmhpdHNfZGlyIDwtICIvVXNlcnMvY2thcG91cmEvZGF0YXNldHMvc2NNRVRfbXMvZ2FzdHJ1bGF0aW9uL21ldHJuYS9oaXRzLyIKcGRmX2RpciA8LSAiL1VzZXJzL2NrYXBvdXJhL2RhdGFzZXRzL3NjTUVUX21zL2dhc3RydWxhdGlvbi9tZXRybmEvYW5hbHlzaXMvIgppZiAoIWRpci5leGlzdHMoaGl0c19kaXIpKSB7ZGlyLmNyZWF0ZShoaXRzX2RpciwgcmVjdXJzaXZlID0gVFJVRSl9CmlmICghZGlyLmV4aXN0cyhwZGZfZGlyKSkge2Rpci5jcmVhdGUocGRmX2RpciwgcmVjdXJzaXZlID0gVFJVRSl9CgojIERlZmluZSBnZW5vbWljIGNvbnRleHQKb3B0cyRhbm5vIDwtIGMoInByb21fMjAwMF8yMDAwIikKI29wdHMkYW5ubyA8LSBjKCJmaXJzdF9pbnRyb24iKQpgYGAKCiMgUk5BIHZhcmlhYmlsaXR5CiMjIExvYWQgQkFTaUNTIG9iamVjdCBhbmQgZ2V0IHBvc3RlcmlvciBzdW1tYXJ5IHN0YXRzCmBgYHtyfQojIExvYWQgQkFTaUNTIG9iamVjdApiYXNpY3Nfb2JqIDwtIHJlYWRSRFMoZmlsZSA9IHBhc3RlMChkYXRhX2RpciwgIi9ybmFfYmFzaWNzLnJkcyIpKQojIFNpbmdsZUNlbGxFeHBlcmltZW50IG9iamVjdApzY2UgPC0gcmVhZFJEUyhpbyRybmEpWywgc2FtcGxlX21ldGFkYXRhJGlkX3JuYV0KIyBQZXJmb3JtIEhWRyBhbmFseXNpcwpIVkcgPC0gQkFTaUNTX0RldGVjdFZHKGJhc2ljc19vYmosIFBlcmNlbnRpbGVUaHJlc2hvbGQgPSAwLjksCiAgICAgICAgICAgICAgICAgICAgICAgRUZEUiA9IDAuMDEsIFBsb3QgPSBGQUxTRSkKaHZnX3Jlc3VsdHMgPC0gSFZHQFRhYmxlWywgYygiR2VuZU5hbWUiLCAiUHJvYiIsICJIVkciKV0KY29sbmFtZXMoaHZnX3Jlc3VsdHMpIDwtIGMoImVuc19pZCIsICJybmFfdGFpbF9wcm9iIiwgInJuYV9pc192YXJpYWJsZSIpCgojIE9idGFpbiBwb3N0ZXJpb3IgZHJhd3Mgc3VtbWFyeQpjaGFpbl9zdW1tYXJ5IDwtIFN1bW1hcnkoYmFzaWNzX29iaikKCiMgTWVyZ2UgRW5zZW1iIElEcyB3aXRoIGdlbmUgc3ltYm9sCnN1YnNldF9tZXRhIDwtIHJvd0RhdGEoc2NlW3Jvd25hbWVzKGNoYWluX3N1bW1hcnlAcGFyYW1ldGVycyRtdSldKSAlPiUKICBhcy5kYXRhLnRhYmxlKGtlZXAucm93bmFtZXMgPSAiZW5zX2lkIikgJT4lCiAgbWVyZ2UoLiwgaHZnX3Jlc3VsdHMsIGJ5ID0gImVuc19pZCIpCgojIEdldCBwb3N0ZXJpb3IgbWVkaWFucyBzdW1tYXJ5CnBvc3Rfc3VtbWFyeSA8LSBkYXRhLnRhYmxlKGVuc19pZCA9IHJvd25hbWVzKGNoYWluX3N1bW1hcnlAcGFyYW1ldGVycyRtdSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJuYV9tdSA9IGxvZyhjaGFpbl9zdW1tYXJ5QHBhcmFtZXRlcnMkbXVbLCAxXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJuYV9lcHNpbG9uID0gY2hhaW5fc3VtbWFyeUBwYXJhbWV0ZXJzJGVwc2lsb25bLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcm5hX2RlbHRhID0gbG9nKGNoYWluX3N1bW1hcnlAcGFyYW1ldGVycyRkZWx0YVssIDFdKSApCmBgYAoKIyMgUGxvdCBSTkEgdmFyaWFiaWxpdHkKIyMjIE1lYW4gLSBvdmVyZGlzcGVyc2lvbiB3aXRoIHRyZW5kIGxpbmUKYGBge3J9CiMgU2hvdyBtZWFuLW92ZXJkaXNwZXJzaW9uIHJlbGF0aW9uc2hpcApwbG90KEJBU2lDU19TaG93Rml0KGJhc2ljc19vYmopKQpgYGAKCiMjIyBNZWFuIC0gb3ZlcmRpc3BlcnNpb24gd2l0aCBIVkcgYW5hbHlzaXMKYGBge3IsIGZpZy53aWR0aD0zLjIsIGZpZy5oZWlnaHQ9Mi41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIENvbWJpbmUgZGF0YSAKam9pbnRfcm5hX2R0IDwtIG1lcmdlKHN1YnNldF9tZXRhLCBwb3N0X3N1bW1hcnksIGJ5ID0gImVuc19pZCIpICU+JQogIHNldG9yZGVyKC1ybmFfZXBzaWxvbikKCnRvLnBsb3QgPC0gam9pbnRfcm5hX2R0W3JuYV9tdSA+IDAuMV0KdG1wIDwtIHRvLnBsb3RbdG8ucGxvdCRybmFfaXNfdmFyaWFibGUgPT0gRkFMU0UsIF0KdG1wIDwtIHRtcFtzYW1wbGUoTlJPVyh0bXApLCAyMDAwKSwgXQp0by5wbG90IDwtIHJiaW5kKHRvLnBsb3RbdG8ucGxvdCRybmFfaXNfdmFyaWFibGUgPT0gVFJVRSwgXSwgdG1wKQoKbW9kZSA8LSBjKCJybmFfZGVsdGEiLCAicm5hX2Vwc2lsb24iKSAKeV9sYWIgPC0gbGlzdCgKICAicm5hX2RlbHRhIiA9ICJSTkEgbG9nIG92ZXJkaXNwZXJzaW9uIiwKICAicm5hX2Vwc2lsb24iID0gIlJOQSByZXNpZHVhbCBvdmVyZGlzcGVyc2lvbiIKKQpmb3IgKG0gaW4gbW9kZSkgewogIGdnIDwtIGdncGxvdCh0by5wbG90LCBhZXNfc3RyaW5nKHggPSAicm5hX211IiwgeSA9IG0pKSArCiAgICBnZW9tX3BvaW50KGFlcyhmaWxsID0gcm5hX2lzX3ZhcmlhYmxlLCBhbHBoYSA9IHJuYV9pc192YXJpYWJsZSwgc2l6ZSA9IHJuYV9pc192YXJpYWJsZSksCiAgICAgICAgICAgICAgIHNoYXBlID0gMjEsIHN0cm9rZSA9IDAuMDEpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyYXk4MCIsInJlZCIpKSArCiAgICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygwLjQsIDAuNikpICsKICAgIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMoMC42NSwgMS42KSkgKwogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMi41KSkpICsKICAgIHhsYWIoIkxvZyBtZWFuIGV4cHJlc3Npb24iKSArIHlsYWIoeV9sYWJbW21dXSkgKwogICAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBoZWFkKHRvLnBsb3Rbcm5hX2lzX3ZhcmlhYmxlID09IFRSVUVdLCBuID0gMjApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlc19zdHJpbmcoeCA9ICJybmFfbXUiLCB5ID0gbSwgbGFiZWwgPSAic3ltYm9sIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKAogICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDEuMSkpLAogICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xNSkpLAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDEpKSwKICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4yKSkKICAgICkKICBwcmludChnZykKICBwZGYoZmlsZSA9IHBhc3RlMChwZGZfZGlyLCAiYmFzaWNzX21lYW52YXJfIiwgbSwgIi5wZGYiKSwgCiAgICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNSwgdXNlRGluZ2JhdHMgPSBGQUxTRSkKICBwcmludChnZykKICBkZXYub2ZmKCkKfQpgYGAKCiMjIyBTdG9yZSBIVkcgcmVzdWx0cwpgYGB7cn0Kcm5hX2h2ZyA8LSBqb2ludF9ybmFfZHRbLCBjKCJlbnNfaWQiLCAic3ltYm9sIiwgInJuYV90YWlsX3Byb2IiLCAicm5hX2lzX3ZhcmlhYmxlIiwKICAgICAgICAgICAgICAgICAgInJuYV9tdSIsICJybmFfZXBzaWxvbiIsICJybmFfZGVsdGEiKV0gJT4lCiAgc2V0bmFtZXMoYygic3ltYm9sIiksIGMoInJuYV9zeW1ib2wiKSkKZndyaXRlKHJuYV9odmcsIHBhc3RlMChoaXRzX2RpciwiL3JuYV9odmcudHh0Lmd6IiksIHNlcCA9ICJcdCIsIHF1b3RlID0gRkFMU0UpCmBgYAoKCiMgRE5BbSB2YXJpYWJpbGl0eQpgYGB7cn0KIyBQZXJmb3JtIEhWRiBhbmFseXNpcwpvYmogPC0gcmVhZFJEUyhzcHJpbnRmKCIlcy8lc192Yi5yZHMiLCBkYXRhX2Rpciwgb3B0cyRhbm5vKSkKc2NtZXRfb2JqIDwtIHNjbWV0X2h2ZihzY21ldF9vYmogPSBvYmosIGRlbHRhX2UgPSAwLjksIGRlbHRhX2cgPSBOVUxMLCBlZmRyID0gMC4xKQoKIyBPYnRhaW4gZ2VuZSBuYW1lcwpnZW5lX21ldGFkYXRhIDwtIGZyZWFkKGlvJGdlbmUubWV0YWRhdGEpICU+JSAuWyxjKCJlbnNfaWQiLCAic3ltYm9sIildCiNIaWdobGlnaHQgdG9wIGhpdHMgZm9yIHByb21vdGVycwptZXRfam9pbnQgPC0gc2NtZXRfb2JqJGh2ZiRzdW1tYXJ5ICU+JSBhcy5kYXRhLnRhYmxlICU+JQogIHNldG5hbWVzKCJmZWF0dXJlX25hbWUiLCAiZW5zX2lkIikgJT4lCiAgbWVyZ2UoZ2VuZV9tZXRhZGF0YSwgYnkgPSAiZW5zX2lkIikgJT4lCiAgc2V0b3JkZXIoLWVwc2lsb24pCmBgYAoKCiMjIE1lYW4gLSBvdmVyZGlzcGVyc2lvbiB3aXRoIEhWRiBhbmFseXNpcwpgYGB7cn0KbW9kZSA8LSBjKCJlcHNpbG9uIiwgImdhbW1hIikKZm9yIChtIGluIG1vZGUpIHsKICAKICBnZyA8LSBzY21ldF9wbG90X21lYW5fdmFyKG9iaiA9IHNjbWV0X29iaiwgeSA9IG0sIHRhc2sgPSAiaHZmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5mZWF0dXJlcyA9IDMwMDApICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC45LCAwLjEpKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBoZWFkKG1ldF9qb2ludCwgbiA9IDM1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzX3N0cmluZyh4ID0gIm11IiwgeSA9IG0sIGxhYmVsID0gInN5bWJvbCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMi4zLCBjb2xvciA9ICJibGFjayIpCiAgcHJpbnQoZ2cpCiAgcGRmKHBhc3RlMChwZGZfZGlyLCAiL3NjbWV0X21lYW52YXJfIiwgb3B0cyRhbm5vLCAiXyIsIG0sICIucGRmIiksCiAgICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNSwgdXNlRGluZ2JhdHMgPSBGQUxTRSkKICBwcmludChnZykKICBkZXYub2ZmKCkKfQpgYGAKCgojIyBTdG9yZSBIVkYgYW5hbHlzaXMgcmVzdWx0cwpgYGB7cn0KbWV0X2h2ZiA8LSBzY21ldF9vYmokaHZmJHN1bW1hcnkgJT4lCiAgYXMuZGF0YS50YWJsZSAlPiUgLlssIGFubm8gOj0gb3B0cyRhbm5vXSAlPiUKICBzZXRuYW1lcygiZmVhdHVyZV9uYW1lIiwgImVuc19pZCIpCmZ3cml0ZShtZXRfaHZmLCBwYXN0ZTAoaGl0c19kaXIsICIvbWV0X2h2Zl8iLCBvcHRzJGFubm8sICIudHh0Lmd6IiksIHNlcCA9ICJcdCIsIHF1b3RlID0gRkFMU0UpCmBgYAoKIyBKb2ludCBhbmFseXNpcwpgYGB7cn0KIyMgQ29tYmluZSBvbWljcwptZXRybmEgPC0gbWVyZ2UobWV0X2h2Ziwgcm5hX2h2ZywgYnkgPSAiZW5zX2lkIikgJT4lIAogIG1lcmdlKGdlbmVfbWV0YWRhdGEsIGJ5ID0gImVuc19pZCIpCgojIFByb2Nlc3MKbWV0cm5hWywgY29sb3IgOj0gImJsYWNrIl0KbWV0cm5hW2Vwc2lsb24gPiAwLjUgJiBybmFfZXBzaWxvbiA+IDEuNSwgY29sb3IgOj0gInJlZCJdCm1ldHJuYVtlcHNpbG9uIDwgMC4xNSAmIHJuYV9lcHNpbG9uID4gMiwgY29sb3IgOj0gImJsdWUiXQptZXRybmEgPC0gbWV0cm5hICU+JSBzZXRvcmRlcigtcm5hX2Vwc2lsb24pCmZ3cml0ZShtZXRybmEsIGZpbGUgPSBwYXN0ZTAoaGl0c19kaXIsICJtZXRybmFfdmFyaWFiaWxpdHlfIiwgb3B0cyRhbm5vLCAiLmNzdiIpLCAKICAgICAgIHNlcCA9ICJcdCIsIHF1b3RlID0gRkFMU0UpCmBgYAoKCiMjIFBsb3QgUk5BIGFuZCBETkEgY28tdmFyaWFiaWxpdHkKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnNldC5zZWVkKDEyKQojIE1hcmtlciBnZW5lcwptYXJrZXJfZ2VuZXMgPC0gYygKICAiQ3VibiIsICJDZXIxIiwgIk1peGwxIiwgIkxlZnR5MiIsICJBbW4iLCAiQ2xkbjYiLCAiTWVzcDEiLAogICJLcnQ4IiwgIkFwb2IiLCAiRm94aTEiLCAiQXBsbnIiLCAiSWQzIiwgIlBlZzMiLCAgIkRwcGE1YSIsICJEcHBhNCIsCiAgIkRwcGEyIiwgIlNwcDEiLCAiTW9yYzEiLCAiVHJhcDFhIiwgIlBjZGgxOSIsICJaZnA0MiIsICJGbXIxbmIiCikKaWYgKG9wdHMkYW5ubyA9PSAicHJvbV8yMDAwXzIwMDAiKSB7CiAgbWFpbl90eHQgPC0gIlByb21vdGVyIHJlZ2lvbiIKfSBlbHNlIGlmIChvcHRzJGFubm8gPT0gImZpcnN0X2V4b24iKSB7CiAgbWFpbl90eHQgPC0gIkZpcnN0IGV4b24iIAp9IGVsc2UgewogIG1haW5fdHh0IDwtICJGaXJzdCBpbnRyb24iCn0KI21haW5fdHh0IDwtIGlmZWxzZShvcHRzJGFubm8gPT0gInByb21fMjAwMF8yMDAwIiwgIlByb21vdGVyIHJlZ2lvbiIsICJGaXJzdCBleG9uIikKIyBDcmVhdGUgY29weQptZXRybmFfcGxvdCA8LSBjb3B5KG1ldHJuYSkKdG1wIDwtIG1ldHJuYV9wbG90W21ldHJuYV9wbG90JGNvbG9yID09ICJibGFjayIsIF0KdG1wIDwtIHRtcFtzYW1wbGUoTlJPVyh0bXApLCBpZmVsc2Uob3B0cyRhbm5vID09ICJwcm9tXzIwMDBfMjAwMCIsIDQwMDAsIDEwMDApKSwgXQp0by5wbG90IDwtIHJiaW5kKG1ldHJuYV9wbG90W21ldHJuYV9wbG90JGNvbG9yICE9ICJibGFjayIsIF0sIHRtcCkKZ2cgPC0gZ2dwbG90KHRvLnBsb3QsIGFlcyh4ID0gZXBzaWxvbiwgeSA9IHJuYV9lcHNpbG9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSBjb2xvciwgZmlsbCA9IGNvbG9yLCBzaXplID0gY29sb3IpKSArCiAgbGFicyh4ID0gZXhwcmVzc2lvbihwYXN0ZSgiUmVzaWR1YWwgb3ZlcmRpc3BlcnNpb24gKEROQW0pIikpLAogICAgICAgeSA9ICJSZXNpZHVhbCBvdmVyZGlzcGVyc2lvbiAoUk5BKSIpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIHN0cm9rZSA9IDAuMSwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiYmxhY2siID0gImdyYXk4MCIsICJyZWQiID0gIiM0RTkzNEQiLCAiYmx1ZSIgPSAiI0M0MDBBRCIpKSArCiAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYygiYmxhY2siID0gMC42LCAicmVkIiA9IDIsICJibHVlIiA9IDIpKSArCiAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiA9IDAuNSwgInJlZCIgPSAwLjgsICJibHVlIiA9IDAuOCkpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvLnBsb3RbY29sb3IgPT0gInJlZCIgJiBzeW1ib2wgJWluJSBtYXJrZXJfZ2VuZXNdLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGVwc2lsb24sIHkgPSBybmFfZXBzaWxvbiwgbGFiZWwgPSBzeW1ib2wpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gNSwgY29sb3IgPSAiIzRFOTM0RCIpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvLnBsb3RbY29sb3IgPT0gImJsdWUiICYgc3ltYm9sICVpbiUgbWFya2VyX2dlbmVzXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBlcHNpbG9uLCAgeSA9IHJuYV9lcHNpbG9uLCBsYWJlbCA9IHN5bWJvbCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA1LCBjb2xvciA9ICIjQzQwMEFEIikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMC45LCAxLjYpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBnZ3RpdGxlKG1haW5fdHh0KSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS40NSkpLAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xKSksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xNSkpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjA1KSksCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjMpKQogICkKcHJpbnQoZ2cpCgpwZGYocGFzdGUwKHBkZl9kaXIsICIvbWV0cm5hX3ZhcmlhYmlsaXR5XyIsIG9wdHMkYW5ubywgIi5wZGYiKSwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNCwgdXNlRGluZ2JhdHMgPSBGQUxTRSkKcHJpbnQoZ2cpCmRldi5vZmYoKQpgYGAKCiMjIFBsb3QgUk5BIGFuZCBETkEgbWVhbiBsZXZlbHMKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnNldC5zZWVkKDEyKQojIE1hcmtlciBnZW5lcwptYXJrZXJfZ2VuZXMgPC0gYygKICAiQ3VibiIsICJDZXIxIiwgIk1peGwxIiwgIkxlZnR5MiIsICJBbW4iLCAiQ2xkbjYiLCAiTWVzcDEiLAogICJLcnQ4IiwgIkFwb2IiLCAiRm94aTEiLCAiQXBsbnIiLCAiSWQzIiwgIlBlZzMiLCAgIkRwcGE1YSIsICJEcHBhNCIsCiAgIkRwcGEyIiwgIlNwcDEiLCAiTW9yYzEiLCAiVHJhcDFhIiwgIlBjZGgxOSIsICJaZnA0MiIsICJGbXIxbmIiCikKCiNtYWluX3R4dCA8LSBpZmVsc2Uob3B0cyRhbm5vID09ICJwcm9tXzIwMDBfMjAwMCIsICJQcm9tb3RlciByZWdpb24iLCAiRmlyc3QgZXhvbiIpCmlmIChvcHRzJGFubm8gPT0gInByb21fMjAwMF8yMDAwIikgewogIG1haW5fdHh0IDwtICJQcm9tb3RlciByZWdpb24iCn0gZWxzZSBpZiAob3B0cyRhbm5vID09ICJmaXJzdF9leG9uIikgewogIG1haW5fdHh0IDwtICJGaXJzdCBleG9uIiAKfSBlbHNlIHsKICBtYWluX3R4dCA8LSAiRmlyc3QgaW50cm9uIgp9CiMgQ3JlYXRlIGNvcHkKbWV0cm5hX3Bsb3QgPC0gY29weShtZXRybmEpCnRtcCA8LSBtZXRybmFfcGxvdFttZXRybmFfcGxvdCRjb2xvciA9PSAiYmxhY2siLCBdCnRtcCA8LSB0bXBbc2FtcGxlKE5ST1codG1wKSwgaWZlbHNlKG9wdHMkYW5ubyA9PSAicHJvbV8yMDAwXzIwMDAiLCA0MDAwLCAxMDAwKSksIF0KdG8ucGxvdCA8LSByYmluZChtZXRybmFfcGxvdFttZXRybmFfcGxvdCRjb2xvciAhPSAiYmxhY2siLCBdLCB0bXApCmdnIDwtIGdncGxvdCh0by5wbG90LCBhZXMoeCA9IG11LCB5ID0gcm5hX211LCAKICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSBjb2xvciwgZmlsbCA9IGNvbG9yLCBzaXplID0gY29sb3IpKSArCiAgbGFicyh4ID0gZXhwcmVzc2lvbihwYXN0ZSgiTWVhbiBETkEgbWV0aHlsYXRpb24iKSksCiAgICAgICB5ID0gIk1lYW4gUk5BIGV4cHJlc3Npb24iKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBzdHJva2UgPSAwLjEsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiA9ICJncmF5ODAiLCAicmVkIiA9ICIjNEU5MzREIiwgImJsdWUiID0gIiNDNDAwQUQiKSkgKwogIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiA9IDAuNiwgInJlZCIgPSAyLCAiYmx1ZSIgPSAyKSkgKwogIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIgPSAwLjUsICJyZWQiID0gMC44LCAiYmx1ZSIgPSAwLjgpKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSB0by5wbG90W2NvbG9yID09ICJyZWQiICYgc3ltYm9sICVpbiUgbWFya2VyX2dlbmVzXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBtdSwgeSA9IHJuYV9tdSwgbGFiZWwgPSBzeW1ib2wpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gNSwgY29sb3IgPSAiIzRFOTM0RCIpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvLnBsb3RbY29sb3IgPT0gImJsdWUiICYgc3ltYm9sICVpbiUgbWFya2VyX2dlbmVzXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBtdSwgIHkgPSBybmFfbXUsIGxhYmVsID0gc3ltYm9sKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDUsIGNvbG9yID0gIiNDNDAwQUQiKSArCiAgYW5ub3RhdGUoInRleHQiLCB5ID0gOSwgeCA9IDAuODgsbGFiZWw9IlIgPSAtMC40IixoanVzdCA9IDEpICsKICAjZ2VvbV9zbW9vdGgobWV0aG9kPWxtLCBzZT1GQUxTRSkgKwogICNjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLCAxLjYpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBnZ3RpdGxlKG1haW5fdHh0KSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS40NSkpLAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xKSksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xNSkpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjA1KSksCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjMpKQogICkKcHJpbnQoZ2cpCgpwZGYocGFzdGUwKHBkZl9kaXIsICIvbWV0cm5hX21lYW5fIiwgb3B0cyRhbm5vLCAiLnBkZiIpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUsIHVzZURpbmdiYXRzID0gRkFMU0UpCnByaW50KGdnKQpkZXYub2ZmKCkKYGBgCgoKIyMgUGxvdCBSTkEgYW5kIEROQSBtZWFuIGxldmVscwpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kc2V0LnNlZWQoMTIpCiMgR2V0IGRlbnNpdHkgb2YgcG9pbnRzIGluIDIgZGltZW5zaW9ucy4KIyBAcGFyYW0geCBBIG51bWVyaWMgdmVjdG9yLgojIEBwYXJhbSB5IEEgbnVtZXJpYyB2ZWN0b3IuCiMgQHBhcmFtIG4gQ3JlYXRlIGEgc3F1YXJlIG4gYnkgbiBncmlkIHRvIGNvbXB1dGUgZGVuc2l0eS4KIyBAcmV0dXJuIFRoZSBkZW5zaXR5IHdpdGhpbiBlYWNoIHNxdWFyZS4KLmdldF9kZW5zaXR5IDwtIGZ1bmN0aW9uKHgsIHksIC4uLikgewogIGRlbnMgPC0gTUFTUzo6a2RlMmQoeCwgeSwgLi4uKQogIGl4IDwtIGZpbmRJbnRlcnZhbCh4LCBkZW5zJHgpCiAgaXkgPC0gZmluZEludGVydmFsKHksIGRlbnMkeSkKICBpaSA8LSBjYmluZChpeCwgaXkpCiAgcmV0dXJuKGRlbnMkeltpaV0pCn0KCiMgZ2dwbG90KHBhcmFtZXRlcl9kZltpbmRfbm90X25hLCBdLCBhZXMobG9nMTAoY3YyX3NjcmFuKSwgbG9nMTAoZGVsdGEpKSkgKyBnZW9tX3BvaW50ZGVuc2l0eSgpICsgc2NhbGVfY29sb3VyX3ZpcmlkaXMobmFtZSA9ICJEZW5zaXR5IikKIyBsaWJyYXJ5KCJnZ3BvaW50ZGVuc2l0eSIpCiMgbGlicmFyeSgidmlyaWRpcyIpCgojIE1hcmtlciBnZW5lcwptYXJrZXJfZ2VuZXMgPC0gYygKICAiQ3VibiIsICJDZXIxIiwgIk1peGwxIiwgIkxlZnR5MiIsICJBbW4iLCAiQ2xkbjYiLCAiTWVzcDEiLAogICJLcnQ4IiwgIkFwb2IiLCAiRm94aTEiLCAiQXBsbnIiLCAiSWQzIiwgIlBlZzMiLCAgIkRwcGE1YSIsICJEcHBhNCIsCiAgIkRwcGEyIiwgIlNwcDEiLCAiTW9yYzEiLCAiVHJhcDFhIiwgIlBjZGgxOSIsICJaZnA0MiIsICJGbXIxbmIiCikKCm1haW5fdHh0IDwtIGlmZWxzZShvcHRzJGFubm8gPT0gInByb21fMjAwMF8yMDAwIiwgIlByb21vdGVyIHJlZ2lvbiIsICJGaXJzdCBleG9uIikKIyBDcmVhdGUgY29weQptZXRybmFfcGxvdCA8LSBjb3B5KG1ldHJuYSkKdG1wIDwtIG1ldHJuYV9wbG90W21ldHJuYV9wbG90JGNvbG9yID09ICJibGFjayIsIF0KI3RtcCA8LSB0bXBbc2FtcGxlKE5ST1codG1wKSwgaWZlbHNlKG9wdHMkYW5ubyA9PSAicHJvbV8yMDAwXzIwMDAiLCA0MDAwLCAxMDAwKSksIF0KdG8ucGxvdCA8LSBtZXRybmFfcGxvdCMgcmJpbmQobWV0cm5hX3Bsb3RbbWV0cm5hX3Bsb3QkY29sb3IgIT0gImJsYWNrIiwgXSwgdG1wKQoKdG8ucGxvdCA8LSB0by5wbG90ICU+JSAuWywgZGVuc2l0eSA6PSAuZ2V0X2RlbnNpdHkobG9nKG11KSwgcm5hX211LCBuID0gNTApXQpnZyA8LSBnZ3Bsb3QodG8ucGxvdCwgYWVzKHggPSBsb2cobXUpLCB5ID0gcm5hX211LCBjb2xvciA9IGRlbnNpdHkpKSArCiAgbGFicyh4ID0gZXhwcmVzc2lvbihwYXN0ZSgiTG9nIG1lYW4gRE5BIG1ldGh5bGF0aW9uIikpLAogICAgICAgeSA9ICJMb2cgbWVhbiBSTkEgZXhwcmVzc2lvbiIpICsKICBnZW9tX3BvaW50KHNpemUgPSAxKSArCiAgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKCkgKwogIHZpcmlkaXM6OnNjYWxlX2NvbG9yX3ZpcmlkaXMoKSArCiAgIyBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvLnBsb3RbY29sb3IgPT0gInJlZCIgJiBzeW1ib2wgJWluJSBtYXJrZXJfZ2VuZXNdLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gbG9nKG11KSwgeSA9IHJuYV9tdSwgbGFiZWwgPSBzeW1ib2wpLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA1LCBjb2xvciA9ICIjNEU5MzREIikgKwogICMgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSB0by5wbG90W2NvbG9yID09ICJibHVlIiAmIHN5bWJvbCAlaW4lIG1hcmtlcl9nZW5lc10sCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBsb2cobXUpLCAgeSA9IHJuYV9tdSwgbGFiZWwgPSBzeW1ib2wpLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA1LCBjb2xvciA9ICIjQzQwMEFEIikgKwogICNhbm5vdGF0ZSgidGV4dCIsIHkgPSA5LCB4ID0gMC44OCxsYWJlbD0iUiA9IC0wLjQiLGhqdXN0ID0gMSkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeSA9IDksIHggPSAwLGxhYmVsPSJSID0gLTAuNCIsaGp1c3QgPSAxKSArCiAgI2dlb21fc21vb3RoKG1ldGhvZD1sbSwgc2U9RkFMU0UpICsKICAjY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKCwgMS42KSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgZ2d0aXRsZShtYWluX3R4dCkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDEuNDUpKSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDEuMSkpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDEuMTUpKSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4wNSkpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4zKSkKICApCnByaW50KGdnKQoKcGRmKHBhc3RlMChwZGZfZGlyLCAiL21ldHJuYV9tZWFuX2hlYXRtYXBfIiwgb3B0cyRhbm5vLCAiLnBkZiIpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUsIHVzZURpbmdiYXRzID0gRkFMU0UpCnByaW50KGdnKQpkZXYub2ZmKCkKYGBgCgo=